//
//  FlipsideViewController.m
//  RunSketch
//
//  Created by Brian on 10/1/09.
//  Copyright Tellart 2009. All rights reserved.
//

#import "NADAMobileIOHelper.h"
#import "FlipsideViewController.h"
#import "BonjourBrowser.h"
#import "MainViewController.h"

#define kBonjourTypeSketchUpdateServer @"_nmsu._tcp"
#define kBonjourTypeRemoteIOPack @"_nmiop._tcp"
#define kBonjourInitialDomain @"local"

@implementation FlipsideViewController

@synthesize
    delegate,
    sketchUpdater,
    sketchServerTextField,
    updateSketchButton,
    updatingLabel,
    updateActivity,
    updateProgress,
    cancelUpdateButton,
    flowControlSwitch,
    iopackBoostSwitch,
    localIOPackEnabledSwitch,
    browser,
    networkConnecting,
    networkError,
    networkConnection,
    localSerialError,
    localSerialConnection,
    
    monitorView,
    monitorScroller,
    sketchOnAudioAmplitude_v,
    sketchOnAudioProfile_l,
    sketchOnAudioProfile_m,
    sketchOnAudioProfile_h,
    sketchOnLocation_lat,
    sketchOnLocation_lon,
    sketchOnLocation_alt,
    sketchOnAccelerate_x,
    sketchOnAccelerate_y,
    sketchOnAccelerate_z,
    sketchOnHeading_heading,
    sketchOnProximity_isClose,
    sketchOnOrientation_orientation,
    sketchOnBattery_level,
    sketchOnIOPackInput_local,
    sketchOnIOPackInput_remote
;


- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];      

    SketchUpdateController *aController = [[SketchUpdateController alloc] init];
    self.sketchUpdater = aController;
    [aController release];
    self.sketchUpdater.delegate = self;
    reloadSketchOnDone = NO;
    
    sketchServerTextField.text = [self.delegate flipsideViewControllerGetRemoteIOPackHostName];
    localIOPackEnabledSwitch.on = [self.delegate flipsideViewControllerGetEnableLocalIOPack];

    [self.monitorScroller setContentSize:CGSizeMake([self.monitorView frame].size.width,  [self.monitorView frame].size.height)];

    [self updateIndicators];
    if (![self.delegate flipsideViewControllerIsRemoteIOPackError] && ([self.sketchServerTextField.text length] > 0)) {
        [self testSketchServer];
    }
    
    if ([self.delegate flipsideViewControllerWasAutoUpdateRequested]) sketchServerTextField.enabled = NO;
}


- (IBAction)done {
    [self cancelSketchUpdate];
	[self.delegate flipsideViewControllerDidFinish:self reloadSketch:reloadSketchOnDone];	
}


/*
 // Override to allow orientations other than the default portrait orientation.
 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
 // Return YES for supported orientations
 return (interfaceOrientation == UIInterfaceOrientationPortrait);
 }
 */

- (void)didReceiveMemoryWarning {
	// Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
	
	// Release any cached data, images, etc that aren't in use.
}

- (void)viewWillUnload
{
    NSLog(@"[FlipsideViewController viewWillUnload]");
}

- (void)viewDidUnload {
	// Release any retained subviews of the main view.
	// e.g. self.myOutlet = nil;
    
    NSLog(@"[FlipsideViewController viewDidUnload]");
    
    self.sketchUpdater = nil;
    
    self.sketchServerTextField = nil;
    self.updateSketchButton = nil;
    self.updatingLabel = nil;
    self.updateActivity = nil;
    self.updateProgress = nil;
    self.cancelUpdateButton = nil;
    self.flowControlSwitch = nil;
    self.iopackBoostSwitch = nil;
    self.localIOPackEnabledSwitch = nil;
    self.browser = nil;
    self.networkConnecting = nil;
    self.networkError = nil;
    self.networkConnection = nil;
    self.localSerialError = nil;
    self.localSerialConnection = nil;

    self.monitorScroller = nil;
    self.monitorView = nil;
    self.sketchOnAudioAmplitude_v = nil;
    self.sketchOnLocation_lat = nil;
    self.sketchOnLocation_lon = nil;
    self.sketchOnLocation_alt = nil;
    self.sketchOnAccelerate_x = nil;
    self.sketchOnAccelerate_y = nil;
    self.sketchOnAccelerate_z = nil;
    self.sketchOnHeading_heading = nil;
    self.sketchOnProximity_isClose = nil;
    self.sketchOnOrientation_orientation = nil;
    self.sketchOnBattery_level = nil;
}


- (void)dealloc {
	[browser release];
    [sketchUpdater release];

    [sketchServerTextField release];
    [updateSketchButton release];
    [updatingLabel release];
    [updateActivity release];
    [updateProgress release];
    [cancelUpdateButton release];
    [flowControlSwitch release];
    [iopackBoostSwitch release];
    [localIOPackEnabledSwitch release];
    [networkConnecting release];
    [networkError release];
    [networkConnection release];
    [localSerialError release];
    [localSerialConnection release];

    [monitorScroller release];
    [monitorView release];
    [sketchOnAudioAmplitude_v release];
    [sketchOnAudioProfile_l release];
    [sketchOnAudioProfile_m release];
    [sketchOnAudioProfile_h release];
    [sketchOnLocation_lat release];
    [sketchOnLocation_lon release];
    [sketchOnLocation_alt release];
    [sketchOnAccelerate_x release];
    [sketchOnAccelerate_y release];
    [sketchOnAccelerate_z release];
    [sketchOnHeading_heading release];
    [sketchOnProximity_isClose release];
    [sketchOnOrientation_orientation release];
    [sketchOnBattery_level release];
    [sketchOnIOPackInput_local release];
    [sketchOnIOPackInput_remote release];

    [super dealloc];
}

- (NSString *)copyStringFromTXTDict:(NSDictionary *)dict which:(NSString*)which {
	// Helper for getting information from the TXT data
	NSData* data = [dict objectForKey:which];
	return data ? [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease] : nil;
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return NO;
}

- (IBAction)showBonjourBrowser
{
    [self.sketchServerTextField resignFirstResponder];

	// Add the controller's view as a subview of the window
	[self.view.window addSubview:[self.browser view]];
    if (self.browser == nil) {
        // Create the Bonjour Browser for Web services
        BonjourBrowser *aBrowser = [[BonjourBrowser alloc] initForType:kBonjourTypeSketchUpdateServer
                                                              inDomain:kBonjourInitialDomain
                                                      customDomains:nil // we won't save any additional domains added by the user
                                           showDisclosureIndicators:NO
                                                   showCancelButton:YES];
        self.browser = aBrowser;
        [aBrowser release];
        self.browser.delegate = self;
        // We want to let the user know that the services list is dynamic and always updating, even when there are no
        // services currently found.
        self.browser.searchingForServicesString = NSLocalizedString(@"Searching for sketch servers...", @"Searching for sketch servers...");
        
        // Add the controller's view as a subview of the window
        [self.view addSubview:[self.browser view]];
        //[self.parentViewController.view insertSubview:[self.browser view] aboveSubview:self.view];
    } else {
        //[self.parentViewController.view exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
        [self.browser view].hidden = NO;
    }
}

- (IBAction)updateSketchFromServer {
    //display an alert to tell the user what to expect on the server
    //UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:@"Open Sketch" message:@"Please select the sketch to open with the file chooser dialog box on the sketch server." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    //[alertView show];
    //[alertView release];

	//tell the view
    [self didStartManualSketchUpdate];
    
    //NSLog([NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://192.168.10.101:8080/"]]);
    NSString *urlString = [NSString stringWithFormat:kSketchManualUpdateURLFormatString, sketchServerTextField.text];

    NSLog(@"Manually updating sketch from %@", urlString);
    NSURL *url = [NSURL URLWithString:urlString];
    [sketchUpdater updateFromURL:url];
    
    //cleanup
    //[urlString release];
}

- (void)autoUpdateSketchFromServer {
    [self didStartAutoSketchUpdate];
    
    //NSLog([NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://192.168.10.101:8080/"]]);
    NSString *urlString = [NSString stringWithFormat:kSketchAutoUpdateURLFormatString, sketchServerTextField.text];

    NSLog(@"Auto-updating sketch from %@", urlString);
    NSURL *url = [NSURL URLWithString:urlString];
    [sketchUpdater updateFromURL:url];
}

- (void)testSketchServer {
    [self didStartSketchServerTest];
    
    NSString *urlString = [NSString stringWithFormat:kSketchCapabilitiesURLFormatString, sketchServerTextField.text];

    NSLog(@"Testing sketch server at %@", urlString);
    NSURL *url = [NSURL URLWithString:urlString];
    [sketchUpdater testFromURL:url];
}

/*
 * cancels a running sketch update
 */
- (IBAction)cancelSketchUpdate {
    if (sketchUpdater.updating || sketchUpdater.doingTest) {
        [sketchUpdater stop];
    }
}

- (void)viewWillDisappear:(BOOL)animated {
    //this is important because it prevents the sketch updater from possibly calling delegate methods
    //on flipsideviewcontroller after it has been removed from the view stack
    if (sketchUpdater) sketchUpdater.delegate = nil;
}


/*
/*
 * hide the update controls and display the cancel controls 
 */
-(void)showCancelUI {
    sketchServerTextField.enabled = NO;
    updateSketchButton.enabled = NO;
    updatingLabel.hidden = NO;
    updateProgress.hidden = YES;
    updateActivity.hidden = NO;
    cancelUpdateButton.hidden = NO;
}

/*
 * hide the cancel controls and display the update controls 
 */
-(void)hideCancelUI {
    updatingLabel.hidden = YES;
    updateActivity.hidden = YES;
    updateProgress.hidden = YES;
    cancelUpdateButton.hidden = YES;
    sketchServerTextField.enabled = YES;
    updateSketchButton.enabled = YES;;
}

- (void) updateIndicators
{
    self.networkConnection.hidden = ![self.delegate flipsideViewControllerIsRemoteIOPackConnected];
    self.networkError.hidden = ![self.delegate flipsideViewControllerIsRemoteIOPackError];

    self.localSerialConnection.hidden = ![self.delegate flipsideViewControllerIsLocalIOPackConnected];
    self.localSerialError.hidden = ![self.delegate flipsideViewControllerIsLocalIOPackError];
    if (!self.localSerialError.hidden) self.localIOPackEnabledSwitch.on = NO;
}

- (IBAction)changedSketchServerAddress
{
    [self.delegate flipsideViewControllerDidChangeRemoteIOPackHostName:self to:self.sketchServerTextField.text];
    [self updateIndicators];
    if (![self.delegate flipsideViewControllerIsRemoteIOPackError] && ([self.sketchServerTextField.text length] > 0)) {
        //test the connection to determine if there is a problem faster than the socket connection can
        [self testSketchServer];
    }
}


- (IBAction)changedLocalIOPackEnabled
{
    [self.delegate flipsideViewControllerDidChangeLocalIOPackEnabled:self to:self.localIOPackEnabledSwitch.on];
    [self updateIndicators];
}


/********************************************************************************/

- (void) bonjourBrowser:(BonjourBrowser*)browser didResolveInstance:(NSNetService*)service {
    //hide the browser
    [self.browser view].hidden = YES;
    if (service == nil) return; //in case we cancelled

	NSDictionary* dict = [[NSNetService dictionaryFromTXTRecordData:[service TXTRecordData]] retain];
    
    NSString *addressFromDesc = [[self copyStringFromTXTDict:dict which:@"address"] retain];
    
    NSLog(@"bonjourBrowser:browser didResolveInstance: addressFromDesc=%@", addressFromDesc);
    
    if (addressFromDesc) {
        self.sketchServerTextField.text = addressFromDesc;
        [self changedSketchServerAddress];
    }
	
	[addressFromDesc release];
	[dict release];
}


/********************************************************************************/

-(void) didStartSketchServerTest
{
    self.updatingLabel.text = @"";
    self.networkConnecting.hidden = NO;
    self.networkConnection.hidden = YES;
}

-(void) didFinishTest: (SketchUpdateController *)c result:(BOOL)passed
{
    self.networkConnecting.hidden = YES;
    self.networkConnection.hidden = !passed;
    self.networkError.hidden = !self.networkConnection.hidden;

    if ([self.delegate flipsideViewControllerWasAutoUpdateRequested]) [self autoUpdateSketchFromServer];
}

-(void) didStartManualSketchUpdate {
    [self showCancelUI];
    self.updatingLabel.text = @"Choose a sketch on the computer";
}

-(void) didStartAutoSketchUpdate {
    [self showCancelUI];
    self.updatingLabel.text = @"Auto-updating...";
}

-(void) didChooseSketch: (SketchUpdateController *)c
{
    self.updatingLabel.text = @"Downloading...";
}

-(void) didFailWithConnectionError: (SketchUpdateController *)c
{
    NSLog(@"FlipsideViewController: didFailWithConnectionError");
    [self hideCancelUI];
    updatingLabel.text = @"Cancelled.";
    updatingLabel.hidden = NO;
}

-(void) didFailWithCommunicationError: (SketchUpdateController *)c message:(NSString *)msg
{
    NSLog(@"FlipsideViewController: didFailWithCommunicationError %@", msg);
    [self hideCancelUI];
    updatingLabel.text = @"Communication error";
    updatingLabel.hidden = NO;
}

-(void) didLoadCatalog: (SketchUpdateController *)c
{
    NSLog(@"FlipsideViewController: didLoadCatalog");
    updatingLabel.text = @"Downloading...";
}

-(void) didFailWithCatalogParserError: (SketchUpdateController *)c error:(NSError *)err
{
    NSLog([NSString stringWithFormat:@"FlipsideViewController: didFailWithCatalogParserError: %@", [err localizedDescription]]);
    [self hideCancelUI];
    updatingLabel.text = @"Invalid catalog";
    updatingLabel.hidden = NO;

    UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:@"Problem Reading Sketch" message:@"It looks like one or more file(s) in your sketch have non-ASCII characters in their file name. Unfortunately, at this time RunSketch cannot understand non-ASCII file names. Hopefully this will be fixed in the future--but for now, you can work around this by renaming any files to use only non-accented Latin characters. If you are working in Dashcode, this may mean that you need to remove such characters from the ID's of any objects in the project. Sorry for the inconvenience." delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil];
    [alertView show];
    [alertView release];
}

-(void) madeProgress: (SketchUpdateController *)c loaded:(int)bytesLoaded total:(int)bytesTotal
{
    float prog = (float)bytesLoaded / (float)bytesTotal;
    NSLog([NSString stringWithFormat:@"FlipsideViewController: madeProgress %i / %i = %f", bytesLoaded, bytesTotal, prog]);
    if ((prog >= 0) && (prog < 1.0)) {
        updateProgress.hidden = NO;
        updateActivity.hidden = YES;
        [updateProgress setProgress:prog];
    }
}

-(void) didSucceed: (SketchUpdateController *)c
{
    NSLog(@"FlipsideViewController: didSucceed");
    NSLog(@"FlipsideViewController: hiding cancel UI");
    [self hideCancelUI];
    updatingLabel.text = @"Sketch opened.";
    updatingLabel.hidden = NO;

    reloadSketchOnDone = YES;
	[self.delegate flipsideViewControllerDidFinish:self reloadSketch:YES];	
}

-(void) didStop: (SketchUpdateController *)c
{
    NSLog(@"FlipsideViewController: didStop");
    NSLog(@"FlipsideViewController: hiding cancel UI");
    [self hideCancelUI];
    updatingLabel.text = @"Cancelled.";
    updatingLabel.hidden = NO;
}

/*******************************************************************************************************************
 *
 * NADAMobileIOHelper delegate methods
 */

- (void)nada:(NADAMobileIOHelper *)nada didAccelerate:(UIAcceleration*)acceleration {
    //NSLog(@"MainViewController: nada didAccelerate");
	//DO NOT USE retainedJavaScriptCall HERE!!!
    self.sketchOnAccelerate_x.text = [NSString stringWithFormat:@"x=%f", acceleration.x];
    self.sketchOnAccelerate_y.text = [NSString stringWithFormat:@"y=%f", acceleration.y];
    self.sketchOnAccelerate_z.text = [NSString stringWithFormat:@"z=%f", acceleration.z];
}

- (void)nada:(NADAMobileIOHelper *)nada didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    //NSLog(@"MainViewController: nada didUpdateToLocation");
    //DO NOT USE retainedJavaScriptCall HERE!!!
    self.sketchOnLocation_lat.text = [NSString stringWithFormat:@"alt=%f", newLocation.coordinate.latitude];
    self.sketchOnLocation_lon.text = [NSString stringWithFormat:@"alt=%f", newLocation.coordinate.longitude];
    self.sketchOnLocation_alt.text = [NSString stringWithFormat:@"alt=%f", newLocation.altitude];
}

#if __IPHONE_3_0
- (void)nada:(NADAMobileIOHelper *)nada didUpdateHeading:(CLHeading *)newHeading {
    //NSLog(@"MainViewController: nada didUpdateHeading");
    self.sketchOnHeading_heading.text = [NSString stringWithFormat:@"heading=%f",
                                [newHeading magneticHeading]
                                ];
}
#endif

- (void)nada:(NADAMobileIOHelper *)nada
        didUpdateAudioAmplitude:(AMIOAmplitudeValue)amp
        average:(AMIOAmplitudeValue)avg 
        low:(AMIOAmplitudeValue)l
        mid:(AMIOAmplitudeValue)m
        high:(AMIOAmplitudeValue)h
{
    //NSLog(@"MainViewController: nada didUpdateAudioAmplitude (%f)", amp);
    self.sketchOnAudioAmplitude_v.text = [NSString stringWithFormat:@"v=%f",
                                amp
                                ];
    self.sketchOnAudioProfile_l.text = [NSString stringWithFormat:@"l=%f",
                                l
                                ];
    self.sketchOnAudioProfile_m.text = [NSString stringWithFormat:@"m=%f",
                                m
                                ];
    self.sketchOnAudioProfile_h.text = [NSString stringWithFormat:@"h=%f",
                                h
                                ];
}

- (void)nada:(NADAMobileIOHelper *)nada didChangeProximity:(BOOL)isNearObject
{
    self.sketchOnProximity_isClose.text = [NSString stringWithFormat:@"isClose=%@",
                                isNearObject ? kJsTRUE : kJsFALSE
                                ];
}

- (void)nada:(NADAMobileIOHelper *)nada didChangeOrientation:(UIDeviceOrientation)orientation
{
    self.sketchOnOrientation_orientation.text = [NSString stringWithFormat:@"orientation=\"%@\"",
                                orientation == UIDeviceOrientationPortrait ? @"portrait" :
                                orientation == UIDeviceOrientationPortraitUpsideDown ? @"portraitUpsideDown" :
                                orientation == UIDeviceOrientationLandscapeLeft ? @"landscapeLeft" :
                                orientation == UIDeviceOrientationLandscapeRight ? @"landscapeRight" :
                                orientation == UIDeviceOrientationFaceUp ? @"faceUp" :
                                orientation == UIDeviceOrientationFaceDown ? @"faceDown" :
                                @"unknown"
                                ];
}

- (void)nada:(NADAMobileIOHelper *)nada didChangeBattery:(float)batteryLevel
{
    self.sketchOnBattery_level.text = [NSString stringWithFormat:@"level=%f",
                                batteryLevel
                                ];
}

- (void)nada:(NADAMobileIOHelper *)nada didReceiveSimpleSocketMessage:(NSString *)msg from:(NSString *)addr
{
    //TODO
}

- (void)nada:(NADAMobileIOHelper *)nada didReceiveFromMessenger:(NSString *)msg from:(NSString *)fromID
{
    //TODO
}

- (void)nada:(NADAMobileIOHelper *)nada iPodDidChangeStatePlaying:(BOOL)playing paused:(BOOL)paused
{
    //TODO
}

- (void)nada:(NADAMobileIOHelper *)nada didChangeIPodVolume:(float)volume
{
    //TODO
}

-(void) updateIOPackMonitor:(NSString *)adddress inputs:(NSString *)inputsTrace outputs:(NSString *)outputsTrace
{
    UILabel *label;
    if ([[adddress substringToIndex:4] isEqualToString:kDeviceAddressSchemeLocal]) {
        label = self.sketchOnIOPackInput_local;
    } else {
        label = self.sketchOnIOPackInput_remote;
    }
    
    label.text = [NSString stringWithFormat:@"@%\n\n%@", outputsTrace, inputsTrace];
}

@end
